package com.wei.czz.framework.common.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.framework.common.handler.redis.RedisHashHandler;
import com.wei.czz.framework.common.dao.IpAddressDao;
import com.wei.czz.framework.common.entity.IpAddressEntity;
import com.wei.czz.framework.common.service.IpAddressService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2021-01-20 21:42:28
 * className: IpAddressServiceImpl ip地址操作请求服务接口实现类
 * version: 1.0
 * description:
 */
@Service("ipAddressService")
@AllArgsConstructor
public class IpAddressServiceImpl extends ServiceImpl<IpAddressDao, IpAddressEntity> implements IpAddressService {

    private static final Logger log = LoggerFactory.getLogger(IpAddressServiceImpl.class);

    private final RestTemplate restTemplate;

    private final RedisHashHandler redisHashHandler;

    private final RedissonClient redissonClient;

    @Override
    public IpAddressEntity getIpAddress(String ip) {
        IpAddressEntity ipAddress;
        if (ip.equals("127.0.0.1")) {
            ipAddress = new IpAddressEntity();
            ipAddress.setPro("本地请求");
            return ipAddress;
        }

        // 缓存键
        String hashKey = CacheKeyUtils.getRedisHashKey("ip_address");

        // 查询Redis缓存，根据ip地址获取真实地址信息
        ipAddress = redisHashHandler.get(hashKey, ip);
        if (ipAddress != null) {
            return ipAddress;
        }
        // 获取Redisson分布式锁对象
        RLock lock = redissonClient.getLock(RedisConstant.LOCK + ip);
        // 加锁30秒
        lock.lock(30, TimeUnit.SECONDS);

        try {
            // 重复查询Redis缓存，根据ip地址获取真实地址信息
            ipAddress = redisHashHandler.get(hashKey, ip);
            if (ipAddress != null) {
                return ipAddress;
            }

            LambdaQueryWrapper<IpAddressEntity> ipAddressLambdaWrapper = new LambdaQueryWrapper<>();
            // 匹配 ip地址 字段
            ipAddressLambdaWrapper.eq(IpAddressEntity::getIp, ip);
            // 查询数据库ip地址关联显示地址表，获取ip地址对应的地址信息
            List<IpAddressEntity> ipAddressList = this.list(ipAddressLambdaWrapper);
            if (!ipAddressList.isEmpty()) {
                if (ipAddressList.size() > 1) {
                    log.warn("系统存在ip关联显示地址重复信息，请处理。ip={}", ip);
                }
                ipAddress = ipAddressList.get(0);

                // 操作Redis缓存，增加ip关联的真实地址信息
                redisHashHandler.put(hashKey, ip, ipAddress);

                return ipAddress;
            }
            // 请求外部接口，获取ip地址关联的现实地址信息
            String responseBody = restTemplate.getForObject(
                    "https://whois.pconline.com.cn/ipJson.jsp?ip={1}&json=true", String.class, ip
            );
            log.info("请求外部接口获取ip地址关联显示地址信息返回结果：{}", responseBody);
            // 请求结果转化
            ipAddress = JSON.parseObject(responseBody, IpAddressEntity.class);
            if (ipAddress == null) {
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "请求源来路不明，请确认，如有问题，请联系客服处理。");
            }
            if (StringUtils.isNotBlank(ipAddress.getErr())) {
                if (StringUtils.isBlank(ipAddress.getAddr())) {
                    throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "未知请求源，请确认，如有问题，请联系客服处理。");
                }
                ipAddress.setPro(ipAddress.getAddr().trim());
            }

            // 操作数据库ip地址关联现实地址表，添加一条记录
            int count = baseMapper.insert(ipAddress);
            if (count == 0) {
                throw new CzzException("添加ip地址关联显示地址信息失败，请务必及时处理");
            }
            // 操作Redis缓存，添加一条ip地址关联现实地址缓存
            redisHashHandler.put(hashKey, ip, ipAddress);
        } finally {
            // 判断当前锁对象是否还持有锁 并 持有该锁的线程与当前业务线程一致
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
        return ipAddress;
    }
}